//
//  Model.m
//  NewtonTest
//
//  Created by Holmes Futrell on 5/19/07.
//  Copyright 2007 Holmes Futrell. All rights reserved.
//

#import "Model.h"
#import "vector.h"

#import "gles20errors.h"

#define MAX_VERTICES 100000
#define MAX_NORMALS MAX_VERTICES
#define MAX_TEX_COORDS MAX_VERTICES
#define MAX_TRIANGLES 300000
#define MAX_QUADS 300000
#define MAX_MATERIAL_SECTIONS 200

@implementation Model

-(id)initWithFile:(NSString *)path scale:(float)scale autoGenNormals:(BOOL)autoGenNormals {
	
	self = [super init];
	
	list = 0;
	
	char cPath[1024];
	[path getFileSystemRepresentation: cPath maxLength: 1024];
	
	num_vertices = num_tex_coords = num_normals = num_triangles = num_quads = num_material_sections = 0;
	
	vec3 *temp_vertices							= (vec3 *)malloc(sizeof(vec3) * MAX_VERTICES);
	vec3 *temp_normals							= (vec3 *)malloc(sizeof(vec3) * MAX_NORMALS);
	vec2 *temp_tex_coords						= (vec2 *)malloc(sizeof(vec2) * MAX_TEX_COORDS);
	triangle *temp_triangles					= (triangle *)malloc(sizeof(triangle) * MAX_TRIANGLES);
	quad *temp_quads							= (quad *)malloc(sizeof(quad) * MAX_QUADS);
	material_section *temp_material_sections	= (material_section *)malloc(sizeof(material_section) * MAX_MATERIAL_SECTIONS);
	
	FILE *f = fopen(cPath, "r");
	
	if (f == NULL) {
		NSLog(@"Couldn't open model %@!", path);
		return NULL;
	}
	else {
		NSLog(@"loading model %@", path);
	}
	
	char line[1024];
	
	while(fgets(line, 1024, f) != NULL) {
		
		vec3 tempv3;
		vec2 tempv2;
		triangle tempt;
		quad tempq;
		char temps[128];
		float tempf;
		
		if (sscanf(line, "v %f %f %f", &tempv3.coords[0], &tempv3.coords[1], &tempv3.coords[2]) == 3) {
			temp_vertices[num_vertices++] = make_vec3(scale * tempv3.coords[0], scale * tempv3.coords[1], scale * tempv3.coords[2]);
		}
		else if (sscanf(line, "vt %f %f %f", &tempv2.coords[0], &tempv2.coords[1], &tempf) == 3) {
			temp_tex_coords[num_tex_coords++] = tempv2;
		}
		else if (sscanf(line, "vt %f %f", &tempv2.coords[0], &tempv2.coords[1]) == 2) {
			temp_tex_coords[num_tex_coords++] = tempv2;
		}
		else if (sscanf(line, "vn %f %f %f", &tempv3.coords[0], &tempv3.coords[1], &tempv3.coords[2]) == 3) {
			temp_normals[num_normals++] = normalize_vec3(tempv3);
		} // quads
		else if (sscanf(line, "f %d %d %d %d", &tempq.v1, &tempq.v2, &tempq.v3, &tempq.v4) == 4) {
			temp_quads[num_quads] = tempq;
			num_quads++;
		}
		else if (sscanf(line, "f %d/%d %d/%d %d/%d %d/%d", \
						&tempq.v1, &tempq.t1, &tempq.v2, &tempq.t2, &tempq.v3, &tempq.t3, &tempq.v4, &tempq.t4) == 8) {
			temp_quads[num_quads] = tempq;
			num_quads++;
		}
		else if (sscanf(line, "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d", \
						&tempq.v1, &tempq.t1, &tempq.n1, &tempq.v2, &tempq.t2, &tempq.n2, &tempq.v3, &tempq.t3, &tempq.n3, &tempq.v4, &tempq.t4, &tempq.n4) == 12) {
			temp_quads[num_quads] = tempq;
			num_quads++;
		} // triangles
		else if (sscanf(line, "f %d %d %d", &tempt.v1, &tempt.v2, &tempt.v3) == 3) {
			temp_triangles[num_triangles] = tempt;
			num_triangles++;
		}
		else if (sscanf(line, "f %d/%d %d/%d %d/%d", &(tempt.v1), &(tempt.t1), &(tempt.v2), &(tempt.t2), &(tempt.v3), &(tempt.t3)) == 6) {
			temp_triangles[num_triangles] = tempt;
			num_triangles++;
		}
		else if (sscanf(line, "f %d/%d/%d %d/%d/%d %d/%d/%d", \
						&tempt.v1, &tempt.t1, &tempt.n1, &tempt.v2, &tempt.t2, &tempt.n2, &tempt.v3, &tempt.t3, &tempt.n3) == 9) {
			temp_triangles[num_triangles] = tempt;
			num_triangles++;
		}
		else if (sscanf(line, "usemtl %s", temps) == 1) {
			temp_material_sections[num_material_sections].start_tri = num_triangles;
			temp_material_sections[num_material_sections].start_quad = num_quads;
			if (num_material_sections != 0) {
				temp_material_sections[num_material_sections-1].end_tri = num_triangles;
				temp_material_sections[num_material_sections-1].end_quad = num_quads;
			}
			num_material_sections++;
		}
		else if (line[0] == '#' || line[0] == '\n') {
			continue;
		}
		else if (sscanf(line, "mtllib %s", temps) == 1) {
			continue;
		}
		else if (sscanf(line, "g %s", temps) == 1) {
			continue;
		}
		else {
			printf("I don't know what to do with %s", line);
		}
		
	}
	
	// close off the final material section
	temp_material_sections[num_material_sections-1].end_tri = num_triangles;
	temp_material_sections[num_material_sections-1].end_quad = num_quads;
	
	/*
	 the .obj file starts with index 1 instead of 0 so we add padding
	 */
	vertices = (vec3 *)malloc(sizeof(vec3) * (num_vertices+1));
	tex_coords = (vec2 *)malloc(sizeof(vec2) * (num_tex_coords+1));
	normals = (vec3 *)malloc(sizeof(vec3) * (num_normals+1));
	triangles = (triangle *)malloc(sizeof(triangle) * num_triangles);
	quads = (quad *)malloc(sizeof(quad) * num_quads);
	material_sections = (material_section *)malloc(sizeof(material_section) * num_material_sections);
	
	memcpy(vertices+1, temp_vertices, sizeof(vec3) * num_vertices);
	memcpy(tex_coords+1, temp_tex_coords, sizeof(vec2) * num_tex_coords);
	memcpy(normals+1, temp_normals, sizeof(vec3) * num_normals);
	memcpy(triangles, temp_triangles, sizeof(triangle) * num_triangles);
	memcpy(quads, temp_quads, sizeof(quad) * num_quads);
	memcpy(material_sections, temp_material_sections, sizeof(material_section) * num_material_sections);
	
	printf("Vertices = %d\n", num_vertices);
	 printf("Tex coords = %d\n", num_tex_coords);
	 printf("Num Normals = %d\n", num_normals);
	 printf("Num Triangles = %d\n", num_triangles);
	 printf("Num Quads = %d\n", num_quads);
	 printf("Num Material sections = %d\n", num_material_sections);
	
	/* delete temporary storage */
	free(temp_vertices);
	free(temp_tex_coords);
	free(temp_normals);
	free(temp_triangles);
	free(temp_quads);
	free(temp_material_sections);
	
	fclose(f);
	
	//[self assembleEdges];
	[self generateFaceNormals];
	//[self generateTangentSpaces];
	
	flat_shading = NO;
	
	
	if (autoGenNormals) {
		[self autoGenNormals];
	}
	
	[self buildBuffer];
	
	
	return self;
	
}

-(void)buildBuffer {
	
	NSLog(@"Build buffer");
	
	glGenBuffers(3, buffers);
	
	int num_indices = 3*num_triangles + 6*num_quads;
	
	GLfloat *raw_vertices			= (GLfloat *)malloc(3 * num_indices * sizeof(GLfloat));
	GLfloat *raw_normals			= (GLfloat *)malloc(3 * num_indices * sizeof(GLfloat));
	GLfloat *raw_texcoords			= (GLfloat *)malloc(2 * num_indices * sizeof(GLfloat));
	
	int i = 0, j = 0, k = 0, l = 0;
	for (i=0; i<num_triangles; i++) {
		memcpy(&raw_normals[k   ], normals[triangles[i].n3].coords, 3 * sizeof(float));
		memcpy(&raw_normals[k+3 ], normals[triangles[i].n2].coords, 3 * sizeof(float));
		memcpy(&raw_normals[k+6 ], normals[triangles[i].n1].coords, 3 * sizeof(float));
		k += 9;
		memcpy(&raw_texcoords[j   ], tex_coords[triangles[i].t3].coords, 2 * sizeof(float));
		memcpy(&raw_texcoords[j+2 ], tex_coords[triangles[i].t2].coords, 2 * sizeof(float));
		memcpy(&raw_texcoords[j+4 ], tex_coords[triangles[i].t1].coords, 2 * sizeof(float));
		j += 6;
		memcpy(&raw_vertices[l   ], vertices[triangles[i].v3].coords, 3 * sizeof(float));
		memcpy(&raw_vertices[l+ 3], vertices[triangles[i].v2].coords, 3 * sizeof(float));
		memcpy(&raw_vertices[l+ 6], vertices[triangles[i].v1].coords, 3 * sizeof(float));
		l += 9;
	}	
	for (i=0; i<num_quads; i++) {
		memcpy(&raw_normals[k   ], normals[quads[i].n4].coords, 3 * sizeof(float));
		memcpy(&raw_normals[k+ 3], normals[quads[i].n2].coords, 3 * sizeof(float));
		memcpy(&raw_normals[k+ 6], normals[quads[i].n1].coords, 3 * sizeof(float));
		memcpy(&raw_normals[k+ 9], normals[quads[i].n4].coords, 3 * sizeof(float));
		memcpy(&raw_normals[k+12], normals[quads[i].n3].coords, 3 * sizeof(float));
		memcpy(&raw_normals[k+15], normals[quads[i].n2].coords, 3 * sizeof(float));
		k += 18;
		memcpy(&raw_texcoords[j+0 ], tex_coords[quads[i].t4].coords, 2 * sizeof(float));
		memcpy(&raw_texcoords[j+2 ], tex_coords[quads[i].t2].coords, 2 * sizeof(float));
		memcpy(&raw_texcoords[j+4 ], tex_coords[quads[i].t1].coords, 2 * sizeof(float));
		memcpy(&raw_texcoords[j+6 ], tex_coords[quads[i].t4].coords, 2 * sizeof(float));
		memcpy(&raw_texcoords[j+8 ], tex_coords[quads[i].t3].coords, 2 * sizeof(float));
		memcpy(&raw_texcoords[j+10], tex_coords[quads[i].t2].coords, 2 * sizeof(float));
		j += 12;
		memcpy(&raw_vertices[l+0 ], vertices[quads[i].v4].coords, 3 * sizeof(float));
		memcpy(&raw_vertices[l+3 ], vertices[quads[i].v2].coords, 3 * sizeof(float));
		memcpy(&raw_vertices[l+6 ], vertices[quads[i].v1].coords, 3 * sizeof(float));
		memcpy(&raw_vertices[l+9 ], vertices[quads[i].v4].coords, 3 * sizeof(float));
		memcpy(&raw_vertices[l+12], vertices[quads[i].v3].coords, 3 * sizeof(float));
		memcpy(&raw_vertices[l+15], vertices[quads[i].v2].coords, 3 * sizeof(float));
		l += 18;
	}	
	
	glBindBuffer(GL_ARRAY_BUFFER, buffers[NORMAL_BUFFER]);
	glBufferData(GL_ARRAY_BUFFER, 3 * num_indices * sizeof(GLfloat), raw_normals, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, buffers[TEXCOORD_BUFFER]);
	glBufferData(GL_ARRAY_BUFFER, 2 * num_indices * sizeof(GLfloat), raw_texcoords, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, buffers[VERTEX_BUFFER]);
	glBufferData(GL_ARRAY_BUFFER, 3 * num_indices * sizeof(GLfloat), raw_vertices, GL_STATIC_DRAW);
	
	free(raw_vertices);
	free(raw_normals);
	free(raw_texcoords);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	
}


-(void)bindBuffers {
	
	// set up vertices
	
	glEnableVertexAttribArray(ATTRIB_VERTEX);
	rb_check_error_simple();
	
	glBindBuffer(GL_ARRAY_BUFFER, buffers[VERTEX_BUFFER]);
	rb_check_error_simple();	
	
	glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, 0, 0, 0);
	rb_check_error_simple();

	// set up normals
	
	glEnableVertexAttribArray(ATTRIB_NORMAL);
	rb_check_error_simple();

	glBindBuffer(GL_ARRAY_BUFFER, buffers[NORMAL_BUFFER]);		
	rb_check_error_simple();
	
	glVertexAttribPointer(ATTRIB_NORMAL, 3, GL_FLOAT, 0, 0, 0);
	rb_check_error_simple();
	
	// set up tex coords
	
	glEnableVertexAttribArray(ATTRIB_TEXCOORDS);
	rb_check_error_simple();
	
	glBindBuffer(GL_ARRAY_BUFFER, buffers[TEXCOORD_BUFFER]);		
	rb_check_error_simple();
	
	glVertexAttribPointer(ATTRIB_TEXCOORDS, 2, GL_FLOAT, 0, 0, 0);
	rb_check_error_simple();
	

}

-(void)drawBuffers {
	
	// Update attribute values.
		
	glDrawArrays(GL_TRIANGLES, 0,  3 * num_triangles + 6 * num_quads);
	rb_check_error_simple();

	
}

-(void)dealloc {
	free(material_sections);
	glDeleteBuffers(3, buffers);
	[super dealloc];
}


@end
